﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BLL
{
    public class PathFinder
    {
        Dictionary<BatteryCenter, double> listDistance = new Dictionary<BatteryCenter, double>();
        Dictionary<BatteryCenter, BatteryCenter> listPrevious = new Dictionary<BatteryCenter, BatteryCenter>();

        public List<BatteryCenterDistance> FindPathFromCityToCity(Map map, BatteryCenter start, BatteryCenter end, double range)
        {
            var from = map.Stations.FirstOrDefault(x => x.name.Equals(start.name));
            var to = map.Stations.FirstOrDefault(x => x.name.Equals(end.name));

            List<BatteryCenterDistance> list = new List<BatteryCenterDistance>();

            if (map.IsAdjacent(from, to))
            {
                list.Add(new BatteryCenterDistance() { BatteryCenter = from, Distance = 0 });
                list.Add(new BatteryCenterDistance() { BatteryCenter = to, Distance = map.FindEdge(from, to).distance });
            }
            else
            {
                CalculateDistance(map, from, range);

                while (listPrevious[to] != null)
                {
                    var b = map.FindEdge(to, listPrevious[to]);

                    list.Add(new BatteryCenterDistance() { BatteryCenter = to, Distance = (b != null ? b.distance : 0) });
                    to = listPrevious[to];
                }

                list.Add(new BatteryCenterDistance() { BatteryCenter = from, Distance = 0 });
                list.Reverse();

                double covered = 0.0;

                BatteryCenterDistance bcd = new BatteryCenterDistance();

                foreach (var b in list)
                {
                    if ((covered + b.Distance >= range) && (b.Distance <= range))
                    {
                        bcd.Stop = true;
                        covered = b.Distance;
                    }
                    else
                        covered += b.Distance;

                    bcd = b;
                }
            }

            return list;
        }

        private void CalculateDistance(Map map, BatteryCenter from, double range)
        {
            var listTemp = new List<BatteryCenter>();

            foreach (var b in map.Stations)
            {
                listDistance[b] = b.Equals(from) ? 0 : Double.PositiveInfinity;
                listPrevious[b] = null;
                listTemp.Add(b);
            }

            while (listTemp.Count > 0)
            {
                var current = listTemp.OrderBy(n => listDistance[n]).First();

                listTemp.Remove(current);

                foreach (var edge in current.Edgelist)
                {
                    var neighbor = map.Stations.FirstOrDefault(x => x.name.Equals(edge.BatteryStation1.name));
                    var alternative = listDistance[current] + edge.distance;

                    if ((alternative < listDistance[neighbor]) && (edge.distance <= range))
                    {
                        listDistance[neighbor] = alternative;
                        listPrevious[neighbor] = current;
                    }
                }
            }
        }
    }
}